package com.dx.fedpauth.common.filter;

import com.dx.fedpauth.common.filter.XssRequestWrapper;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * 将各种XSS攻击的文本传入XSS过滤器，测试过滤的效果。
 */
public class XssFilterTest {

    private String attack, filtered;

    /**
     * 普通文本，带有Markdown语法
     */
    @Test
    public void normalText() {
        attack = "底部工具栏\n" +
                "# ion-nav-view\n" +
                "当用户在你的app中浏览时，Ionic能够保持检测他们的浏览历史。通过了解他们的浏览历史，向左或向右滑动时可以正确的在视图间转换，或不转换。一个额外的好处是Ionic的导航系统具有可以管理多个历史记录的能力。\n" +
                "\n" +
                "Ionic利用AngularUI路由模块，使应用程序接口可以组织成不同的“状态”。例如Angular的核心`$route`服务，利用URL控制视图。然而，AngularUI路由提供了一个更强大的状态管理，即状态可以被命名，嵌套， 以及合并视图，允许一个以上模板呈现在同一个页面。此外，每个状态无需绑定到一个URL，并且数据可以更灵活地推送到每个状态。\n" +
                "\n" +
                "用`ionNavView`指令在你的app中渲染模版。每个模板都是状态的一部分。状态通常映射到一个url上，然后用`angular-ui-router`定义程序（查看它们的文档，记下用`ion-nav-view`替换`ui-view`的例子）。\n" +
                "\n";
        filtered = XssRequestWrapper.stripXSS(attack);
        assertThat(filtered).isEqualTo(attack);
    }

    /**
     * Markdown语法带有代码块，代码块中的内容不会改变
     */
    @Test
    public void markdownCode() {
        attack = "用法\n" +
                "在这个例子中，我们将创建一个应用程序中包含不同状态的导航视图。\n" +
                "\n" +
                "要做到这一点，在我们的标签中，用ionNavView顶层指令。要显示一个页眉，我们利用，当我们通过导航堆栈导航时，就会用ionNavBar指令更新。\n" +
                "\n" +
                "你可以在navView的动画属性上应用任何动画类来给它添加页面动画。\n" +
                "\n" +
                "建议的页面过渡： `slide-left-right`, `slide-left-right-ios7`, `slide-in-up`。\n" +
                "\n" +
                "```html\n" +
                "  <ion-nav-bar></ion-nav-bar>\n" +
                "    <ion-nav-view animation=\"slide-left-right\">\n" +
                "    <!-- 中间内容 -->\n" +
                "  </ion-nav-view>\n" +
                "```\n" +
                "接下来，我们需要设置被渲染的状态。\n" +
                "\n" +
                "```html\n" +
                "  var app = angular.module('myApp', ['ionic']);\n" +
                "  app.config(function($stateProvider)  {\n" +
                "    $stateProvider.state('index',  {\n" +
                "      url: '/',\n" +
                "      templateUrl: 'home.html'\n" +
                "    }).state('music',  {\n" +
                "      url: '/music',\n" +
                "      templateUrl: 'music.html'\n" +
                "    });\n" +
                "  });\n" +
                "```\n" +
                "然后在app启动时， `$stateProvider`就会检查url，检查它的索引匹配状态，然后尝试将home.html加载到 `<ion-nav-view>`内。\n" +
                "\n" +
                "页面由指定的URL加载。在Angular中有一个简单的方式就是把它们直接放到你的HTML文件，并用 `<script type=\"text/ng-template\">`语法。因此，这是一种把home.html载入到app中的一种方式:\n";
        filtered = XssRequestWrapper.stripXSS(attack);
        assertThat(filtered).isEqualTo(attack);
    }


    /**
     * OWASP网站上的攻击示例
     */
    @Test
    public void owaspCheatsheet() {
        // 普通XSS注入
        attack = "<SCRIPT SRC=http://xss.rocks/xss.js></SCRIPT>";
        filtered = XssRequestWrapper.stripXSS(attack);
        assertThat(filtered).isEmpty();

        // 无害的版本
        attack = "(SCRIPT SRC=http://xss.rocks/xss.js)(/SCRIPT)";
        filtered = XssRequestWrapper.stripXSS(attack);
        assertThat(filtered).isEqualTo(attack);

        // 多边形攻击
        attack = "javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/\"/+/onmouseover=1/+/[*/[]/+alert(1)//'>";
        filtered = XssRequestWrapper.stripXSS(attack);
        assertThat(filtered).isEqualTo("javascript:/*-->");

        // 图片标签执行代码
        attack = "<IMG SRC=\"javascript:alert('XSS');\">";
        filtered = XssRequestWrapper.stripXSS(attack);
        assertThat(filtered).isEqualToIgnoringCase("<IMG>");
    }

    /**
     * 混合了代码块和非代码块，在非代码块的位置发起攻击会被过滤，在代码块的位置发起攻击会被保留
     */
    @Test
    public void combinedCase() {
        attack = "# Basic Usage\n" +
                "Cards are primarily <IMG SRC=\"javascript:alert('XSS');\"> a CSS component. To use a basic card, follow this structure:\n" +
                "```html\n" +
                "<ion-card>\n" +
                "\n" +
                "  <ion-card-header>\n" +
                "    Card Header <IMG SRC=\"javascript:alert('XSSA');\"> a CSS component\n" +
                "  </ion-card-header> \n" +
                "\n" +
                "  <ion-card-content>\n" +
                "    <!-- Add card content here! -->\n" +
                "  </ion-card-content>\n" +
                "\n" +
                "</ion-card>\n" +
                "```\n" +
                "# Card Headers\n" +
                "`<IMG SRC=\"javascript:alert('XSSB');\">` <IMG SRC=\"javascript:alert('XSSC');\">Just like a normal page, cards can be customized to include headers. To add a header to a card, add the `<ion-card-header>` component inside of your card:";
        filtered = XssRequestWrapper.stripXSS(attack);
        // 代码块中的攻击语句javascript:alert被保留
        assertThat(filtered).contains("javascript:alert('XSSA')");
        assertThat(filtered).contains("javascript:alert('XSSB')");
        // 非代码块中的攻击语句javascript:alert被消除
        assertThat(filtered).doesNotContain("javascript:alert('XSS')");
        assertThat(filtered).doesNotContain("javascript:alert('XSSC')");
        // 非代码块中的正常内容被保留
        assertThat(filtered).contains(" add the `<ion-card-header>` component");
    }

    /**
     * 文本含有表情符号，令MySQL不能识别
     */
    @Test
    public void emojiInText() {
        String cucumberDoc = "Dependency Injection \uD83D\uDD17︎\n" +
                "While it’s not required, we strongly recommend you include one of the dependency injection modules as well. ";
        String expected = "Dependency Injection ︎\n" +
                "While it’s not required, we strongly recommend you include one of the dependency injection modules as well. ";
        String actual = XssRequestWrapper.stripXSS(cucumberDoc);
        assertThat(actual).isEqualTo(expected);
    }

}
